import datetime
import os
import threading
import time

import customtkinter as ctk
from CTkMessagebox import CTkMessagebox

from src.core.qhd import Qhd
from src.utils.AESUtil import AESUtil  # 导入AES加密模块

# 添加默认密钥（建议后续通过环境变量或更安全方式管理）
DEFAULT_AES_KEY = "gdqy_zz03_secure"  # 长度需为16/24/32，此处为16个字符

# 设置外观模式和颜色主题
ctk.set_appearance_mode("Dark")  # 使用固定深色主题避免Linux下的检测问题
ctk.set_default_color_theme("blue")  # 可选 "blue", "green", "dark-blue"

DEFAULT_FONT = None

class QhdGUICustom:
    def __init__(self):
        """初始化GUI界面"""
        self.app = ctk.CTk()
        self.app.title("0.3抢活动程序")
        
        # 设置窗口关闭事件处理
        self.app.protocol("WM_DELETE_WINDOW", self._on_closing)
        
        # 获取缩放因子
        scaling = ctk.ScalingTracker.get_window_dpi_scaling(self.app)
        
        # 计算缩放后的尺寸以保持物理尺寸一致
        # 基础尺寸为500x500，通过除以缩放因子来补偿系统的自动缩放
        base_width = 600
        base_height = 800
        window_width = int(base_width / scaling)
        window_height = int(base_height / scaling)
        
        self.app.geometry(f"{window_width}x{window_height}")
        self.app.resizable(True, True)  # 允许调整窗口大小
        
        # 设置最小窗口尺寸
        min_width = int(400 / scaling)
        min_height = int(400 / scaling)
        self.app.minsize(min_width, min_height)
        
        # 设置日志最大行数
        self.max_log_lines = 500

        # 居中窗口
        self.center_window()
        
        self.qhd = Qhd(self.append_output)
        self.is_executing = False
        
        # 定时功能相关变量
        self.scheduled_time = None
        self.scheduler_thread = None
        self.is_scheduling = False
        
        # 创建密码文件路径
        self.password_file =  "passwd.enc"  # 加密文件

        # 创建控件
        self.create_widgets()
        
        # 检查并加载密码文件
        self.check_password_file()

        self._lock = threading.Lock()
        
    def center_window(self):
        """居中窗口"""
        self.app.update_idletasks()
        width = self.app.winfo_width()
        height = self.app.winfo_height()
        x = (self.app.winfo_screenwidth() // 2) - (width // 2)
        y = (self.app.winfo_screenheight() // 2) - (height // 2)
        self.app.geometry(f'{width}x{height}+{x}+{y}')
        
    def create_widgets(self):
        """创建所有界面组件"""
        # 主框架
        self.main_frame = ctk.CTkFrame(self.app, corner_radius=10)
        self.main_frame.pack(padx=20, pady=20, fill="both", expand=True)
        
        # 标题
        font_config = {"size": 20, "weight": "bold"}
        if DEFAULT_FONT:
            font_config["family"] = DEFAULT_FONT
        self.title_label = ctk.CTkLabel(
            self.main_frame, 
            text="0.3抢活动程序", 
            font=ctk.CTkFont(**font_config)
        )
        self.title_label.pack(pady=(20, 30))
        
        # 输入区域框架
        self.input_frame = ctk.CTkFrame(self.main_frame, corner_radius=10)
        self.input_frame.pack(padx=20, pady=10, fill="x")
        
        # 学号输入
        font_config = {"size": 14}
        if DEFAULT_FONT:
            font_config["family"] = DEFAULT_FONT
        self.uid_label = ctk.CTkLabel(self.input_frame, text="学号:", font=ctk.CTkFont(**font_config))
        self.uid_label.pack(pady=(15, 5), anchor="w", padx=20)
        
        self.entry_uid = ctk.CTkEntry(self.input_frame, placeholder_text="请输入学号", height=35, font=ctk.CTkFont(**font_config))
        self.entry_uid.pack(pady=(0, 10), padx=20, fill="x")
        
        # 密码输入
        self.passwd_label = ctk.CTkLabel(self.input_frame, text="密码:", font=ctk.CTkFont(**font_config))
        self.passwd_label.pack(pady=(5, 5), anchor="w", padx=20)
        
        self.entry_passwd = ctk.CTkEntry(
            self.input_frame, 
            placeholder_text="请输入密码", 
            show="*", 
            height=35,
            font=ctk.CTkFont(**font_config)
        )
        self.entry_passwd.pack(pady=(0, 15), padx=20, fill="x")
        
        # 活动ID输入 (初始隐藏)
        self.wid_label = ctk.CTkLabel(self.input_frame, text="活动ID:", font=ctk.CTkFont(**font_config))
        self.wid_entry = ctk.CTkEntry(
            self.input_frame, 
            placeholder_text="请输入活动ID", 
            height=35,
            font=ctk.CTkFont(**font_config)
        )
        
        # 定时功能框架
        self.schedule_frame = ctk.CTkFrame(self.main_frame, fg_color="transparent")
        self.schedule_frame.pack(pady=5, fill="x", padx=20)
        
        # 定时执行复选框
        self.schedule_var = ctk.BooleanVar()
        self.schedule_checkbox = ctk.CTkCheckBox(
            self.schedule_frame,
            text="定时执行",
            variable=self.schedule_var,
            command=self.toggle_schedule,
            font=ctk.CTkFont(**font_config)
        )
        self.schedule_checkbox.pack(side="left", padx=(0, 10))
        
        # 日期选择
        self.date_label = ctk.CTkLabel(self.schedule_frame, text="日期:", font=ctk.CTkFont(size=12))
        self.date_label.pack(side="left", padx=(10, 5))
        
        # 默认日期设为明天
        tomorrow = datetime.date.today() + datetime.timedelta(days=1)
        self.year_var = ctk.StringVar(value=str(tomorrow.year))
        self.month_var = ctk.StringVar(value=f"{tomorrow.month:02d}")
        self.day_var = ctk.StringVar(value=f"{tomorrow.day:02d}")
        
        self.year_spinbox = ctk.CTkEntry(
            self.schedule_frame, 
            textvariable=self.year_var, 
            width=50, 
            justify='center',
            font=ctk.CTkFont(size=12)
        )
        self.year_spinbox.pack(side="left", padx=2)
        self.year_label = ctk.CTkLabel(self.schedule_frame, text="年", font=ctk.CTkFont(size=12))
        self.year_label.pack(side="left")
        
        self.month_spinbox = ctk.CTkEntry(
            self.schedule_frame, 
            textvariable=self.month_var, 
            width=40, 
            justify='center',
            font=ctk.CTkFont(size=12)
        )
        self.month_spinbox.pack(side="left", padx=2)
        self.month_label = ctk.CTkLabel(self.schedule_frame, text="月", font=ctk.CTkFont(size=12))
        self.month_label.pack(side="left")
        
        self.day_spinbox = ctk.CTkEntry(
            self.schedule_frame, 
            textvariable=self.day_var, 
            width=40, 
            justify='center',
            font=ctk.CTkFont(size=12)
        )
        self.day_spinbox.pack(side="left", padx=2)
        self.day_label = ctk.CTkLabel(self.schedule_frame, text="日", font=ctk.CTkFont(size=12))
        self.day_label.pack(side="left")
        
        # 时间选择
        self.time_label = ctk.CTkLabel(self.schedule_frame, text="时间:", font=ctk.CTkFont(size=12))
        self.time_label.pack(side="left", padx=(10, 5))
        
        # 默认时间设为上午9点
        self.hour_var = ctk.StringVar(value="09")
        self.minute_var = ctk.StringVar(value="00")
        
        self.hour_spinbox = ctk.CTkEntry(
            self.schedule_frame, 
            textvariable=self.hour_var, 
            width=40, 
            justify='center',
            font=ctk.CTkFont(size=12)
        )
        self.hour_spinbox.pack(side="left", padx=2)
        self.hour_label = ctk.CTkLabel(self.schedule_frame, text="时", font=ctk.CTkFont(size=12))
        self.hour_label.pack(side="left")
        
        self.minute_spinbox = ctk.CTkEntry(
            self.schedule_frame, 
            textvariable=self.minute_var, 
            width=40, 
            justify='center',
            font=ctk.CTkFont(size=12)
        )
        self.minute_spinbox.pack(side="left", padx=2)
        self.minute_label = ctk.CTkLabel(self.schedule_frame, text="分", font=ctk.CTkFont(size=12))
        self.minute_label.pack(side="left")
        
        # 初始化定时控件状态
        self.update_schedule_controls_state()
        
        # 按钮框架
        self.button_frame = ctk.CTkFrame(self.main_frame, fg_color="transparent")
        self.button_frame.pack(pady=10, fill="x", padx=20)
        
        # 访问活动广场按钮
        self.btn_square = ctk.CTkButton(
            self.button_frame,
            text="访问活动广场",
            command=self.open_square,
            height=40,
            font=ctk.CTkFont(**font_config),
            fg_color="#20bf6b",
            hover_color="#1a9c58",
            corner_radius=8
        )
        self.btn_square.pack(fill="x", pady=5)
        
        # 登录按钮
        font_config = {"size": 14, "weight": "bold"}
        if DEFAULT_FONT:
            font_config["family"] = DEFAULT_FONT
        self.btn_login = ctk.CTkButton(
            self.button_frame,
            text="登录",
            command=self.login,
            height=40,
            font=ctk.CTkFont(**font_config),
            corner_radius=8
        )
        self.btn_login.pack(fill="x", pady=5)
        
        # 执行按钮 (初始隐藏)
        font_config = {"size": 14, "weight": "bold"}
        if DEFAULT_FONT:
            font_config["family"] = DEFAULT_FONT
        self.btn_execute = ctk.CTkButton(
            self.button_frame,
            text="执行",
            command=self.execute,
            height=40,
            font=ctk.CTkFont(**font_config),
            fg_color="#3a7ff6",
            hover_color="#2a5fc9",
            corner_radius=8
        )
        
        # 停止按钮 (初始隐藏)
        self.btn_stop = ctk.CTkButton(
            self.button_frame,
            text="停止",
            command=self.stop_execution,
            height=40,
            font=ctk.CTkFont(**font_config),
            fg_color="#ff4d4f",
            hover_color="#cc3333",
            corner_radius=8
        )
        
        # 输出区域框架
        self.output_frame = ctk.CTkFrame(self.main_frame, corner_radius=10)
        self.output_frame.pack(padx=20, pady=(10, 20), fill="both", expand=True)
        
        # 输出标题
        font_config = {"size": 14, "weight": "bold"}
        if DEFAULT_FONT:
            font_config["family"] = DEFAULT_FONT
        self.output_label = ctk.CTkLabel(
            self.output_frame, 
            text="操作日志", 
            font=ctk.CTkFont(**font_config)
        )
        self.output_label.pack(pady=(10, 5))
        
        # 输出文本框
        self.output_text = ctk.CTkTextbox(
            self.output_frame, 
            corner_radius=8,
            font=ctk.CTkFont(family="Noto Sans CJK SC" if not DEFAULT_FONT else DEFAULT_FONT, size=12),
            wrap="word"
        )
        self.output_text.pack(padx=15, pady=(0, 15), fill="both", expand=True)
        self.output_text.configure(state="disabled")
        
    def save_password_to_file(self, uid, passwd):
        """保存账号密码到加密文件"""
        try:
            # 使用AES加密账号密码
            encrypted_uid = AESUtil.encrypt(uid, DEFAULT_AES_KEY)
            encrypted_passwd = AESUtil.encrypt(passwd, DEFAULT_AES_KEY)
            
            # 写入加密文件
            with open(self.password_file, "w", encoding="utf-8") as f:
                f.write(f"{encrypted_uid}\n{encrypted_passwd}\n")
            self.append_output("[信息] 账号密码已加密保存到本地文件", 'info')
        except Exception as e:
            self.append_output(f"[错误] 保存密码文件失败: {str(e)}", 'error')

    def check_password_file(self):
        """检查并加载加密的密码文件"""
        if os.path.exists(self.password_file):
            try:
                with open(self.password_file, "r", encoding="utf-8") as f:
                    lines = f.readlines()
                    if len(lines) >= 2:
                        encrypted_uid = lines[0].strip()
                        encrypted_passwd = lines[1].strip()
                        
                        # 解密
                        uid = AESUtil.decrypt(encrypted_uid, DEFAULT_AES_KEY)
                        passwd = AESUtil.decrypt(encrypted_passwd, DEFAULT_AES_KEY)
                        
                        self.entry_uid.insert(0, uid)
                        self.entry_passwd.insert(0, passwd)
                        # 询问用户是否自动登录
                        msg = CTkMessagebox(title="自动登录", message="检测到保存的账号密码，是否自动登录？", option_1="否", option_2="是", icon="question")
                        if msg.get() == "是":
                            self.login()
            except Exception as e:
                self.append_output(f"[错误] 读取或解密密码文件失败: {str(e)}", 'error')
                
    def login(self):
        """登录按钮回调函数"""
        uid = self.entry_uid.get()
        passwd = self.entry_passwd.get()
        
        if not uid or not passwd:
            self.append_output("[错误] 学号和密码不能为空！", 'error')
            return
            
        # 禁用输入框和按钮
        self.entry_uid.configure(state="disabled")
        self.entry_passwd.configure(state="disabled")
        self.btn_login.configure(state="disabled")
        
        def login_thread():
            # 开始登陆
            if self.qhd.login(username=uid, password=passwd):
                # 保存账号密码到文件
                self.save_password_to_file(uid, passwd)
                # 显示执行相关控件
                self.show_execution_controls()
            else:
                # 恢复输入框和按钮
                self.entry_uid.configure(state="normal")
                self.entry_passwd.configure(state="normal")
                self.btn_login.configure(state="normal")
                
        threading.Thread(target=login_thread, daemon=True).start()
        
    def show_execution_controls(self):
        """显示执行相关的控件"""
        # 隐藏密码输入框，显示活动ID输入框
        self.passwd_label.pack_forget()
        self.entry_passwd.pack_forget()
        
        self.wid_label.pack(pady=(15, 5), anchor="w", padx=20)
        self.wid_entry.pack(pady=(0, 15), padx=20, fill="x")
        
        # 隐藏登录按钮，显示执行按钮
        self.btn_login.pack_forget()
        self.btn_execute.pack(fill="x", pady=5)
        
    def toggle_schedule(self):
        """切换定时功能开关"""
        self.update_schedule_controls_state()
        if self.schedule_var.get():
            self.append_output("[信息] 定时功能已启用", 'info')
        else:
            self.cancel_scheduled_task()
            self.append_output("[信息] 定时功能已禁用", 'info')
            
    def update_schedule_controls_state(self):
        """更新定时控件的状态"""
        state = "normal" if self.schedule_var.get() else "disabled"
        self.year_spinbox.configure(state=state)
        self.month_spinbox.configure(state=state)
        self.day_spinbox.configure(state=state)
        self.hour_spinbox.configure(state=state)
        self.minute_spinbox.configure(state=state)
        
        # 更新标签颜色
        labels = [self.date_label, self.year_label, self.month_label, self.day_label,
                  self.time_label, self.hour_label, self.minute_label]
        for label in labels:
            if ctk.get_appearance_mode() == "Dark":
                label.configure(text_color=("gray70" if state == "disabled" else "white"))
            else:
                label.configure(text_color=("gray30" if state == "disabled" else "black"))
        
    def schedule_execution(self):
        """安排定时执行任务"""
        try:
            year = int(self.year_var.get())
            month = int(self.month_var.get())
            day = int(self.day_var.get())
            hour = int(self.hour_var.get())
            minute = int(self.minute_var.get())
            
            # 验证日期和时间的有效性
            try:
                scheduled_date = datetime.date(year, month, day)
            except ValueError as e:
                self.append_output("[错误] 日期无效，请检查年月日", 'error')
                self.schedule_var.set(False)
                self.update_schedule_controls_state()
                return
            
            if not (0 <= hour <= 23):
                self.append_output("[错误] 小时必须在0-23之间", 'error')
                self.schedule_var.set(False)
                self.update_schedule_controls_state()
                return
                
            if not (0 <= minute <= 59):
                self.append_output("[错误] 分钟必须在0-59之间", 'error')
                self.schedule_var.set(False)
                self.update_schedule_controls_state()
                return
                
            # 创建完整的预定时间
            scheduled_time = datetime.datetime(year, month, day, hour, minute)
            
            # 检查设定的时间是否早于当前时间
            now = datetime.datetime.now()
            if scheduled_time <= now:
                self.append_output("[错误] 设定时间不能早于当前时间", 'error')
                self.schedule_var.set(False)
                self.update_schedule_controls_state()
                return
                
            self.scheduled_time = scheduled_time
            
            # 启动定时线程
            self.is_scheduling = True
            self.scheduler_thread = threading.Thread(target=self._scheduler_worker, daemon=True)
            self.scheduler_thread.start()
            
            self.append_output(f"[信息] 已设置定时任务，将在 {scheduled_time.strftime('%Y-%m-%d %H:%M')} 执行", 'info')
            
        except ValueError:
            self.append_output("[错误] 请输入有效的时间格式", 'error')
            self.schedule_var.set(False)
            self.update_schedule_controls_state()
            
    def _scheduler_worker(self):
        """定时任务的工作线程"""
        while self.is_scheduling and self.scheduled_time:
            now = datetime.datetime.now()
            if now >= self.scheduled_time:
                # 时间到了，执行任务
                self.app.after(0, self._execute_scheduled_task)
                break
            time.sleep(1)  # 每秒检查一次
            
    def _execute_scheduled_task(self):
        """执行定时任务"""
        self.append_output("[信息] 定时时间已到，开始执行任务", 'info')
        self.schedule_var.set(False)
        self.update_schedule_controls_state()
        # 执行实际的抢活动任务
        self._execute_actual_task()
            
    def cancel_scheduled_task(self):
        """取消定时任务"""
        self.is_scheduling = False
        self.scheduled_time = None
        self.append_output("[信息] 已取消定时任务", 'info')
        
    def execute(self):
        """执行按钮回调函数"""
        with self._lock:
            if self.is_scheduling or self.is_executing:
                return
            # 如果启用了定时功能且没有正在调度，则安排任务
            if self.schedule_var.get() and not self.is_scheduling:
                self.schedule_execution()
                # 即使是定时任务，也要触发原本的界面交互逻辑
                self._prepare_for_execution()
                return

            # 直接执行抢活动任务
            self._execute_actual_task()
        
    def _prepare_for_execution(self):
        """准备执行任务，更新UI状态"""
        # 禁用活动ID输入框
        self.wid_entry.configure(state="disabled")
        # 隐藏执行按钮，显示停止按钮
        self.btn_execute.pack_forget()
        self.btn_stop.pack(fill="x", pady=5)
        
    def _execute_actual_task(self):
        """执行实际的抢活动任务"""
        wid = self.wid_entry.get()
        if not wid:
            self.append_output("[错误] 活动ID不能为空！", 'error')
            self.on_execution_complete()  # 恢复UI状态
            return
            
        # 启动执行线程
        flag = self.qhd.start_grab(wid, stop_callback=self.on_execution_complete)

        if flag:
            self.is_executing = True
            # 更新UI状态（如果还没有更新）
            self._prepare_for_execution()

    def stop_execution(self):
        """停止按钮回调函数"""
        with self._lock:
            flag = True
            # 停止定时任务（如果正在定时）
            if self.is_scheduling:
                self.cancel_scheduled_task()

            # 停止实际的抢活动任务（如果正在执行）
            if self.is_executing:
                flag = self.qhd.stop_grab()

            if flag:
                # 恢复UI状态
                self.on_execution_complete()
        
    def on_execution_complete(self):
        """执行完成或停止后的回调"""
        self.is_executing = False
        self.is_scheduling = False  # 确保定时状态也被重置
        self.btn_stop.pack_forget()
        self.btn_execute.pack(fill="x", pady=5)
        self.wid_entry.configure(state="normal")

    def append_output(self, message, msg_type='normal'):
        """向输出框追加内容"""
        # 使用after方法确保GUI更新在主线程中进行
        self.app.after(0, self._safe_append_output, message, msg_type)

    def _safe_append_output(self, message, msg_type):
        """安全地更新输出框内容"""
        try:
            # 定义消息类型对应的颜色
            colors = {
                'normal': 'white' if ctk.get_appearance_mode() == "Dark" else 'black',
                'error': '#ff4d4f',
                'info': '#3a7ff6',
                'warning': '#faad14',
                'success': '#00cc66'
            }
            color = colors.get(msg_type, colors['normal'])

            # 启用编辑
            self.output_text.configure(state="normal")

            # 插入消息
            self.output_text.insert("end", message + "\n", msg_type)
            print(message)

            # 检查日志行数，如果超过最大行数则删除最旧的行
            lines = self.output_text.get("1.0", "end").splitlines()
            if len(lines) > self.max_log_lines:
                # 删除多余的行（保留最新的max_log_lines行）
                excess_lines = len(lines) - self.max_log_lines
                for _ in range(excess_lines):
                    self.output_text.delete("1.0", "2.0")  # 删除第一行

            # 配置标签颜色
            self.output_text.tag_config(msg_type, foreground=color)
            # 滚动到底部
            self.output_text.see("end")
            # 禁用编辑
            self.output_text.configure(state="disabled")
            # 更新界面
            self.app.update_idletasks()
        except Exception as e:
            # 忽略GUI更新时的异常（如程序已退出或组件被销毁）
            pass

    def open_square(self):
        """打开活动广场"""
        import webbrowser
        webbrowser.open("https://study.gdip.edu.cn/CloudPortal/CloudSquare")
        self.append_output("[信息] 正在打开活动广场页面...", 'info')
        
    def run(self):
        """运行应用程序"""
        self.app.mainloop()

    def _on_closing(self):
        """处理窗口关闭事件"""
        # 先执行停止逻辑
        self.stop_execution()
        
        # 延迟关闭应用程序，确保停止逻辑执行完成
        self.app.after(100, self._destroy_app)
    
    def _destroy_app(self):
        """销毁应用程序"""
        try:
            self.app.destroy()
        except:
            pass  # 忽略可能的异常

if __name__ == "__main__":
    gui = QhdGUICustom()
    gui.run()
